#pragma once

#include"Common.h"

const int DELAY = 3;

float GetEdgeDistance(UnitType type1 , Position position1 , UnitType type2 , Position position2)
{
	float dx = 0.0;
	float dy = 0.0;
	const float top = position1.y() - type1.dimensionUp();
	const float bottom = position1.y() + type1.dimensionDown();
	const float anotherTop = position2.y() - type2.dimensionUp();
	const float anotherBottom = position2.y() + type2.dimensionDown();
	dy = top - anotherBottom;
	if(dy < 0.0)
	{
		dy = anotherTop - bottom;
		if(dy < 0.0)
		{
			dy = 0.0;
		}
	}

	const float left = position1.x() - type1.dimensionLeft();
	const float right = position1.x() + type1.dimensionRight();
	const float anotherLeft = position2.x() - type2.dimensionLeft();
	const float anotherRight = position2.x() + type2.dimensionRight();
	dx = left - anotherRight;
	if(dx < 0.0)
	{
		dx = anotherLeft - right;
		if(dx < 0.0)
		{
			dx = 0.0;
		}
	}

	return sqrtf(dx * dx + dy * dy);
}

Position PredictFuturePosition(const Unit* unit , const int frameCount)
{
	Position answer(unit->getPosition());
	answer.x() += (int)(unit->getVelocityX() * (float)(frameCount));
	answer.y() += (int)(unit->getVelocityY() * (float)(frameCount));
	return answer;
}

class HydraliskControler
{
public:
	HydraliskControler(Unit* hydralisk)
		:_me(hydralisk),
		_target(NULL),
		_targetPosition(hydralisk->getPosition()),
		_mission(Idle),
		_attackState(ChaseAndCanAttack),
		_frameToWait(0),
		_canAttack(true),
		_canMove(true)
	{
	}
	void OnFrame()
	{
		PrintInfomation();

		switch(_attackState)
		{
		case ChaseAndCanAttack:
			break;
		case WaitForCheck:
			WaitForCheckFunction();
			break;
		case WaitAttackFrame:
			WaitAttackFrameFunction();
			break;
		case ChaseDontAttack:
			ChaseDontAttackFunction();
			break;
		}

		if(_me->getOrderTarget() != NULL && _me->getOrderTarget()->exists())
		{
			_mission = AttackUnit;
			_target = _me->getOrderTarget();
		}

		switch(_mission)
		{
		case Idle:
			break;
		case AttackUnit:
			if(_target == NULL || ( _target != NULL && !_target->exists()))
			{
				_mission = Idle;
			}
			else if(_attackState == ChaseAndCanAttack)
			{
				ChaseAttackFunction();
			}
			break;
		case MoveToPosition:
			if(_me->getPosition().getDistance(_targetPosition) < 5.0)
			{
				_mission = Idle;
			}
			else if(_canMove)
			{
				_me->move(_targetPosition);
			}
			break;
		case AttackMove:
			if(_attackState == ChaseAndCanAttack)
			{
				_me->attack(_targetPosition);
			}
			break;
		}
	}
	void AttackMoveTo(const Position& targetPosition)
	{
		_mission = AttackMove;
		_targetPosition = targetPosition;
	}
	void Attack(Unit* target)
	{
		if(target != NULL && target->exists())
		{
			_mission = AttackUnit;
			_target = target;
		}
	}
	void Move(const Position& targetPosition)
	{
		_mission = MoveToPosition;
		_targetPosition = targetPosition;
		_targetPosition.makeValid();
	}
	bool CanAttack() const
	{
		return _canAttack;
	}
	bool CanMove() const
	{
		return _canMove;
	}
	bool IsIdle() const
	{
		return _mission == Idle;
	}
private:
	enum Mission{Idle , AttackUnit , MoveToPosition , AttackMove};
	enum AttackState{ChaseAndCanAttack , WaitForCheck , WaitAttackFrame , ChaseDontAttack};
	void ChaseAttackFunction()
	{
		Position enemyPositionAfter3FramePrediction(PredictFuturePosition(_target , 3));
		Position myPositionAfter3FramePrediction(PredictFuturePosition(_me , 3));
		if(GetEdgeDistance(_me->getType() , myPositionAfter3FramePrediction , _target->getType() , enemyPositionAfter3FramePrediction) <= _me->getType().groundWeapon().maxRange())
		{
			_me->attack(_target);
			_frameToWait = DELAY;
			a = 0;
			_attackState = WaitForCheck;
			_canAttack = false;
			_canMove = false;
		}
		else
		{
			Position answer;
			Location myLocation(_me->getPosition());
			Location targetLocation(_target->getPosition());
			myLocation.MoveTowardLocation(targetLocation , myLocation.GetDistance(targetLocation) + 60.0f);
			answer = myLocation.ToBWAPI_Position();

			_me->move(answer);

			Broodwar->drawLineMap(_me->getPosition().x() , _me->getPosition().y() , answer.x() , answer.y() , Colors::Green);

			_canAttack = true;
			_canMove = true;
		}
	}
	void WaitForCheckFunction()
	{
		if(_me->getGroundWeaponCooldown() > 0)
		{
			++a;
			if(a >= 2)
			{
				_me->stop();
				_frameToWait = 5;
				_attackState = WaitAttackFrame;
				_canAttack = false;
				_canMove = false;
			}
		}
		else
		{
			--_frameToWait;
			if(_frameToWait <= 0)
			{
				_attackState = ChaseAndCanAttack;
				_canAttack = true;
				_canMove = true;
			}
		}
	}
	void WaitAttackFrameFunction()
	{
		--_frameToWait;
		if(_frameToWait <= 0)
		{
			_attackState = ChaseDontAttack;
			_canAttack = false;
			_canMove = true;
		}
	}
	void ChaseDontAttackFunction()
	{
		if(_me->getGroundWeaponCooldown() <= DELAY + 1)
		{
			_attackState = ChaseAndCanAttack;
			_canAttack = true;
			_canMove = true;
		}
	}
	void PrintInfomation() const
	{
		if(!_me->isSelected())
		{
			return;
		}
		Position unitPosition = _me->getPosition();
		Broodwar->drawTextMap(unitPosition.x() , _me->getTop() - 12 , "Cool down: %d" , _me->getGroundWeaponCooldown());

		string missionName;
		if(_mission == Idle){missionName = "Idle";}
		else if(_mission == AttackUnit){missionName = "AttackUnit";}
		else if(_mission == MoveToPosition){missionName = "MoveToPosition";}
		else if(_mission == AttackMove){missionName = "AttackMove";}
		Broodwar->drawTextMap(unitPosition.x() , _me->getTop() - 20 , "mission: %s" , missionName.c_str());

		string s;
		if(_attackState == ChaseAndCanAttack){s = "ChaseAndCanAttack";}
		else if(_attackState == WaitForCheck){s = "WaitForCheck";}
		else if(_attackState == WaitAttackFrame){s = "WaitAttackFrame";}
		else if(_attackState == ChaseDontAttack){s = "ChaseDontAttack";}
		Broodwar->drawTextMap(unitPosition.x() , _me->getTop() - 28 , "attack statet: %s" , s.c_str());

		Broodwar->drawTextMap(unitPosition.x() , _me->getTop() - 36 , "%s" , (_canMove?"CanMove":"Can Not Move"));
		Broodwar->drawTextMap(unitPosition.x() , _me->getTop() - 42 , "%s" , (_canAttack?"CanAttack":"Can Not Attack"));

		if(_target != NULL && _target->exists())
		{
			Broodwar->drawLineMap(_me->getPosition().x() , _me->getPosition().y() , _target->getPosition().x() , _target->getPosition().y() , Colors::Green);
		}

		Broodwar->drawLineMap(_me->getPosition().x() , _me->getPosition().y() , _targetPosition.x() , _targetPosition.y() , Colors::Yellow);
	}
	//--
	int a;
	Unit* _me;
	Unit* _target;
	Position _targetPosition;
	AttackState _attackState;
	Mission _mission;
	int _frameToWait;
	bool _canAttack;
	bool _canMove;
	Position lastPrediction;
};